package com.zxy.supplier_system.server.service

import com.zxy.supplier_system.server.controller.MarketShoppingGuideSalaryDocumentController
import com.zxy.supplier_system.server.controller.MarketShoppingGuideSalaryDocumentController.MonthlySalaryGroupByEmployeeResponse
import com.zxy.supplier_system.server.controller.MarketShoppingGuideSalaryDocumentController.PreviewMonthlySalaryDocumentsResponse.EmployeeSalary
import com.zxy.supplier_system.server.controller.QueryMarketShoppingGuideSalaryDocumentGroupByMonthResponse
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.repository.MarketSaleDocumentRepository
import com.zxy.supplier_system.server.repository.MarketShoppingGuideSalaryPlanRepository
import com.zxy.supplier_system.server.repository.MarketShoppingGuideSalaryPlanResultRepository
import com.zxy.supplier_system.server.utils.orElse404
import com.zxy.supplier_system.server.utils.toIntIdAndName
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.math.BigDecimal
import kotlin.math.max

@Service
class MarketShoppingGuideSalaryDocumentService(
    private val marketSaleDocumentRepository: MarketSaleDocumentRepository,
    private val systemServiceService: SystemServiceService,
    private val marketShoppingGuideSalaryPlanRepository: MarketShoppingGuideSalaryPlanRepository,
    private val marketShoppingGuideSalaryPlanResultRepository: MarketShoppingGuideSalaryPlanResultRepository,
) {
    fun queryMarketShoppingGuideSalaryDocuments(
        marketId: Int,
        request: MarketShoppingGuideSalaryDocumentController.QueryMarketShoppingGuideSalaryDocumentRequest
    ): Page<MarketShoppingGuideSalaryDocumentController.QueryMarketShoppingGuideSalaryDocumentResponse> {

        TODO()
    }

    @Transactional
    fun findMonthlySalarySummary(
        marketId: Int,
        pageable: Pageable
    ): Page<QueryMarketShoppingGuideSalaryDocumentGroupByMonthResponse> {
        return this.marketShoppingGuideSalaryPlanResultRepository.findMonthlySalarySummary(marketId, pageable)
    }

    @Transactional(readOnly = true)
    fun previewMonthlySalaryDocuments(
        marketId: Int,
        year: Int,
        month: Int
    ): MarketShoppingGuideSalaryDocumentController.PreviewMonthlySalaryDocumentsResponse {
        val systemService = systemServiceService.getCurrentSystemService()
        val documents =
            this.marketSaleDocumentRepository.findAllByAccount_Market_IdAndAccount_Market_SystemServiceAndYearAndMonth(
                marketId,
                systemService,
                year,
                month
            )
        val allGoodsList = documents.flatMap { document ->
            document.goodsList
        }

        val totalSale = allGoodsList.sumOf { it.totalFee }
        val plan =
            this.marketShoppingGuideSalaryPlanRepository.findByMarketIdAndMarket_SystemService(marketId, systemService)
                .orElse404()
        val sortedRules = plan.rules.sortByCondition()

        val ruleSaleItemMap =
            // 分类销售记录到不同的提成工资方案
            computeRuleSaleItemMap(allGoodsList, sortedRules)
        val ruleTotalSaleMap = ruleSaleItemMap.map { e ->
            val rule = e.key
            val totalSale = e.value.sumOf { it.totalFee }
            rule to totalSale
        }.toMap()
        val ruleWithNoteAndSalaryMap = sortedRules.associateWith {
            var note: String? = null
            val salary = if (it.commissionPercentage == null) {
                it.fixedSalary!!
            } else {
                val decimal = ruleTotalSaleMap[it] ?: BigDecimal.ZERO
                note = "销量${decimal},提成${it.commissionPercentage}%"
                it.commissionPercentage!! * decimal / BigDecimal(100)
            }
            NoteWithSalary(
                note,
                salary,
            )
        }

        val shoppingGuides = plan.market.shoppingGuides

        val shoppingGuideCount = max(shoppingGuides.size, (1))
        // 提成平均后的各项
        val ruleWithNoteAndMeanSalaryMap = ruleWithNoteAndSalaryMap.map { (rule, noteWithSalary) ->
            if (rule.commissionPercentage == null) {
                rule to noteWithSalary
            } else {
                rule to NoteWithSalary(
                    "${noteWithSalary.note},${shoppingGuides.size}人均分",
                    noteWithSalary.salary / shoppingGuideCount.toBigDecimal()
                )
            }
        }.toMap()
        val employeeItemNoteWithSalaryList =
            ruleWithNoteAndMeanSalaryMap.map { EmployeeSalary.Item(it.value.salary, it.value.note, it.key.name) }
                .toMutableList()

        val inMinimumSalary =
            ruleWithNoteAndMeanSalaryMap.filter { it.key.inMinimum }.map { it.value }.sumOf { it.salary }
        val employeeMeanTotalSalary = if (plan.minimumSalary != null && inMinimumSalary < plan.minimumSalary) {
            employeeItemNoteWithSalaryList.add(
                EmployeeSalary.Item(
                    plan.minimumSalary!!,
                    "计入保底工资为:$inMinimumSalary,触发保底",
                    "保底工资"
                )
            )
            plan.minimumSalary!!
        } else {
            inMinimumSalary
        } + ruleWithNoteAndMeanSalaryMap.filter { !it.key.inMinimum }.map { it.value }.sumOf { it.salary }
        val employeeSalaries = shoppingGuides.map { shoppingGuide ->
            EmployeeSalary(
                shoppingGuide.employee.id!!,
                shoppingGuide.employee.name,
                employeeMeanTotalSalary,
                employeeItemNoteWithSalaryList
            )
        }
        return MarketShoppingGuideSalaryDocumentController.PreviewMonthlySalaryDocumentsResponse(
            employeeSalaries,
            shoppingGuideCount.toBigDecimal() * employeeMeanTotalSalary,
            totalSale,
            ruleWithNoteAndSalaryMap.map {
                MarketShoppingGuideSalaryDocumentController.PreviewMonthlySalaryDocumentsResponse.RuleSalary(
                    it.key.id!!,
                    it.key.name,
                    it.value.note,
                    it.value.salary
                )
            }
        )
    }


    /**
     * 计算保底
     */
    @Transactional
    fun generateMarketShoppingGuideSalaryDocuments(marketId: Int, year: Int, month: Int) {
        val systemService = systemServiceService.getCurrentSystemService()
        this.marketShoppingGuideSalaryPlanResultRepository.deleteByMarket_SystemServiceAndMarket_IdAndYearAndMonth(
            systemService,
            marketId,
            year,
            month
        )
        val documents =
            this.marketSaleDocumentRepository.findAllByAccount_Market_IdAndAccount_Market_SystemServiceAndYearAndMonth(
                marketId,
                systemService,
                year,
                month
            )
        val allGoodsList = documents.flatMap { document ->
            document.goodsList
        }
        val plan =
            this.marketShoppingGuideSalaryPlanRepository.findByMarketIdAndMarket_SystemService(marketId, systemService)
                .orElse404()
        val market = plan.market
        val shoppingGuides = market.shoppingGuides

        val sortedRules = plan.rules.sortByCondition()
        val shoppingGuideCount = max(shoppingGuides.size, (1))
        // 分类销售记录到不同的提成工资方案
        val ruleSaleItemMap =
            computeRuleSaleItemMap(allGoodsList, sortedRules)
        val ruleAndMeanSalaryMap = sortedRules.associate {
            it to if (it.commissionPercentage == null) {
                it.fixedSalary!!
            } else {
                (ruleSaleItemMap[it]?.sumOf { item -> item.totalFee }
                    ?: BigDecimal.ZERO) * it.commissionPercentage!! / BigDecimal(100) / shoppingGuideCount.toBigDecimal()
            }
        }
        val inMinimumSalary =
            ruleAndMeanSalaryMap.filter { it.key.inMinimum }.map { it.value }.sumOf { it }

        fun generateNote(rule: MarketShoppingGuideSalaryRule): String? {
            return if (rule.commissionPercentage == null) {
                null
            } else {
                val decimal = (ruleSaleItemMap[rule]?.sumOf { item -> item.totalFee }
                    ?: BigDecimal.ZERO)
                "销量${decimal},提成${rule.commissionPercentage}%,${shoppingGuides.size}人均分"
            }
        }

        val generateMarketShoppingGuideSalaryDocumentItem: () -> List<MarketShoppingGuideSalaryDocumentItem> =
            if (plan.minimumSalary != null && inMinimumSalary < plan.minimumSalary) {
                {
                    mutableListOf(MarketShoppingGuideSalaryDocumentItem().apply {
                        this.salary = plan.minimumSalary!!
                        this.name = "保底工资"
                        this.note = "计入保底工资为:$inMinimumSalary,触发保底"
                    }) + ruleAndMeanSalaryMap.filterNot { it.key.inMinimum }.map {
                        MarketShoppingGuideSalaryDocumentItem().apply {
                            this.salary = it.value
                            this.name = it.key.name
                            this.note = generateNote(it.key)
                        }
                    }
                }
            } else {
                {
                    ruleAndMeanSalaryMap.map {
                        MarketShoppingGuideSalaryDocumentItem().apply {
                            this.name = it.key.name
                            this.salary = it.value
                            this.note = generateNote(it.key)
                        }
                    }
                }
            }

        val result = this.marketShoppingGuideSalaryPlanResultRepository.save(MarketShoppingGuideSalaryPlanResult().apply {
            this.market = market
            this.year = year
            this.month = month
            val that = this
            this.documents = market.shoppingGuides.map { shoppingGuide ->
                MarketShoppingGuideSalaryDocument().apply {

                    this.shoppingGuide = shoppingGuide.employee
                    this.items = generateMarketShoppingGuideSalaryDocumentItem()
                    this.result = that
                }
            }.toMutableList()
            this.minimumSalary = plan.minimumSalary
            this.rules = plan.rules.map { MarketShoppingGuideSalaryPlanResult.MarketShoppingGuideSalaryRuleData.fromEntity(it) }
        })



    }

    companion object{
        fun computeRuleSaleItemMap(
            allGoodsList: List<MarketSaleDocumentItem>,
            sortedRules: List<MarketShoppingGuideSalaryRule>
        ): Map<MarketShoppingGuideSalaryRule, List<MarketSaleDocumentItem>> = allGoodsList.groupBy { item ->
            sortedRules.find { rule ->
                rule.commissionPercentage != null && rule.goodsInScope(item.goods)
            }
        }
            .filter { it.key != null }
            .map { it.key!! to it.value }
            .toMap()
    }

    data class NoteWithSalary(val note: String?, val salary: BigDecimal)

    @Transactional(readOnly = true)
    fun findMonthlySalaryGroupByEmployee(
        marketId: Int,
        year: Int,
        month: Int
    ): List<MonthlySalaryGroupByEmployeeResponse> {
        return this.marketShoppingGuideSalaryPlanResultRepository.findByMarket_SystemServiceAndMarket_IdAndYearAndMonth(
            systemServiceService.getCurrentSystemService(),
            marketId,
            year,
            month
        ).orElse404().documents .map {
            MonthlySalaryGroupByEmployeeResponse(it.id!!, it.shoppingGuide.toIntIdAndName(), it.items.map { item ->
                MonthlySalaryGroupByEmployeeResponse.Item(item.id!!, item.salary, item.name, item.note)
            }, it.note)
        }
    }
}

